/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fsktms.messagemanager;

import fsktms.packetmanager.IPacketManager;
import fsktms.packetmanager.PacketFactory;
import fsktms.IMessage;
import fsktms.IPacket;
import fsktms.Message;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.*;

public class MessageManager implements IMessageManager {

    protected final IPacketManager packageMan;
    protected Map<Integer, Message> waittingMessage = new HashMap<>();
    protected Queue<Message> completedMessage = new ArrayDeque<>();
    
    private Random indexHashRadom = new Random();

    public MessageManager(IPacketManager pakageMan)
    {
        this.packageMan = pakageMan;
    }

    private void CheckHasPackage()
    {
        while (!this.isClosed())
        {
            synchronized (packageMan)
            {
                packageMan.run();

                if (packageMan.isHasNewPacket())
                {
                    IPacket pack;
                    pack = packageMan.getLastPacket();
                    switch (pack.getType()) {
                        case 0:
                            int messageMK = ByteBuffer.wrap(pack.getData()).getInt();
                            waittingMessage.put(pack.getIndex(), new Message(this.packageMan.getAddress(), messageMK));
                            break;
                        case 1:
                            Message currentMessage = waittingMessage.get(pack.getIndex());
                            if (currentMessage != null) {
                                currentMessage.appenData(pack.getData());
                            }
                            break;
                        case 2:
                            Message comMessage = waittingMessage.get(pack.getIndex());
                            waittingMessage.remove(pack.getIndex());
                            if (comMessage != null)
                            {
                                comMessage.join();
                                completedMessage.add(comMessage);
                            }
                            break;
                        default:
                            System.out.println("Sent package's type not found");
                            break;
                    }
                }
            }
        }
    }

    @Override
    public IMessage getCompletedMessage() {
        return this.completedMessage.poll();
    }

    @Override
    public boolean isHasCompletedMessage() {
        return completedMessage.size() > 0;
    }

    @Override
    public void run()
    {
        this.CheckHasPackage();
    }

    private int sendCount = 0;
    @Override
    public void sendMessage(final IMessage message)
    {
        // Create thread for sent
        Thread sentThread = new Thread()
        {
            @Override
            public void run()
            {
                ((Message) message).split();
                // Generate index
                int newIndex = hashIndex(message.getHostAddress(), message.getDestAddress(), sendCount);
                try
                {
                    packageMan.sendPackage(PacketFactory.createStartPacket(newIndex, message.getMagicKey()));
                    
                    while (message.isHasNextDataPacket()) 
                    {
                        packageMan.sendPackage(PacketFactory.createChunkPacket(message.getNextDataPacket(), newIndex));
                    }

                    packageMan.sendPackage(PacketFactory.createEndPacket(newIndex));
                }
                catch (IOException ex)
                {
                }
            }
        };
        sentThread.start();
        
        ++sendCount;
    }

    @Override
    public void close() throws IOException
    {
        this.packageMan.close();
    }
    
    @Override
    public boolean isClosed()
    {
        return this.packageMan.isClosed();
    }

    @Override
    public String getAddress()
    {
        return this.packageMan.getAddress();
    }
    
    private int hashIndex(String s1, String s2, int c)
    {
        return String.valueOf(s1.hashCode() + s2.hashCode() + c + indexHashRadom.nextInt()).hashCode();
    }
}